package de.koller.aframe.util.misc;

import de.koller.aframe.util.math.FunctionPreview;
import de.koller.aframe.util.math.IFunction;
import de.koller.oceanFramework.OceanException;
import java.util.Random;

public class ProbabilityRandom {

	public static boolean showProgress = false;
	
	private final Random rnd;
	private final int segments;
	private double maxDist;
	private final double[] distribution;
	
	public ProbabilityRandom( Random rnd, int segments ) {
		this.rnd = rnd;
		this.segments = segments;
		distribution = new double[ segments ];
		
		double[] d = new double[ segments ];
		for( int i = 0; i < distribution.length; i++ )
			d[i] = 1;
		setDistribution( d );
	}
	
	public void setDistribution( double... dist ) {
		if( segments != dist.length )
			throw new OceanException( "distribution have to be the same size!" );
		
		double n = 0;
		for( int i = 0; i < dist.length; i++ ) {
			distribution[i] = n;
			n += dist[i];
		}
		
		maxDist = n;
	}
	
	public int nextSegment() {
		double r = rnd.nextDouble() * maxDist;
		for( int i = 0; i < segments; i++ )
			if( checkSegment( i, r ) )
				return i;
		throw new OceanException();
	}
	
	private boolean checkSegment( int i, double r ) {
		double max = (i+1) >= segments ? maxDist : distribution[i+1];
		double min = distribution[i];
		
		return r >= min && r < max;
	}
	
	public void testDistribution( int iterations, boolean abs ) {
		new Distribution( iterations ).show( abs );
	}
	
	private class Distribution implements IFunction {

		private final int iterations;
		private int[] counts = new int[ segments ];
		private boolean abs;
		
		private Distribution( int iterations ) {
			this.iterations = iterations;
			for( int i = 0; i < iterations; i++ ) {
				counts[ nextSegment() ]++;
				if( showProgress && (i % 10_000_000 == 0) )
					System.out.println( i + " / " + iterations );
			}
			
		}
		
		private void show( boolean abs ) {
			this.abs = abs;
			FunctionPreview.showFunction( -.1f, segments + .1f, this );
		}

		@Override
		public float getValue( float x ) {
			if( x < 0 ) return 0;
			if( x > segments ) return abs ? iterations : 1;
			
			int c = (int)Math.floor( x );
			if( !abs )
				return (float)counts[c] / iterations;
			else
				return counts[c];
		}
		
	}
	
}
